package com.cskaoyan.javase.oop1._10block._2building;

/**
 * @description: 构造代码块
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

/**
 * > 什么是构造代码块
 * 定义在类的成员位置，使用以下声明方式声明的代码块，称之为构造代码块
 * {} (仅仅使用一个大括号定义)
 *
 * 构造代码块的作用: 随着构造器的执行而执行,用来给成员变量赋值
 * 总结一下成员变量赋值的方式(创建对象过程中的赋值方式):
 *      1.默认初始化,具有默认值
 *      2.显式赋值
 *      3.构造代码块赋值
 *      4.构造器赋值
 * 以上赋值方式,"掐头去尾",默认初始化是最先执行的,而构造器是最后执行的
 * 这里我们需要探究显式赋值和构造代码块赋值的先后顺序:
 *      通过代码现象,我们发现,显式赋值和构造代码块赋值按照代码的书写顺序从上到下执行
 *      写在下面的后执行,是结果,写在上面的赋值就被覆盖掉了
 *
 * 注意: 把构造代码块赋值写在成员变量定义显式赋值的上面,虽然语法不会报错,也能正常执行,但终究不太符合逻辑,会让代码看起来不太正常. 所以建议正常情况下,不要把构造代码块赋值写在显式赋值的上面,尽量写在下面(除非是出面试题)
 *
 * 为什么说构造代码块随着构造器的执行而执行?
 * 通过查看反编译class文件，我们发现编译后的代码中并不存在构造代码块的结构，而是：
 * **直接将成员变量的显式赋值和构造代码块中的代码智能地加入，类所有的构造器中的前几行**
 * 解释:
 * 1.所谓智能是为了保证：
 * - 成员变量的显式赋值和构造代码块，按照代码的书写顺序从上到下执行
 * 2.构造代码块中的代码会加入类的所有构造器的前几行,
 *      所以new对象时,构造器仅会调用某一个,但是不论调用哪个构造器,构造代码块中的代码统统都会执行
 * 3.根据特点2,如果有一些代码在new对象时一定都要调用,可以提取"公因式",放进构造代码块中
 *
 * 需求: 记录创建一个类的对象的次数
 *      首先记录次数需要一个变量,int count,它应该是一个属于类的,在类的全局仅有一个的静态成员变量
 *      有了变量以后,就要每创建一次对象,该count++一次
 *      可以把count++代码写入构造代码块中,不论使用哪个构造器创建对象,它都会++一次
 *
 * > 一些使用构造代码块需要注意的地方：
 * 1. 建议应该将构造代码块，放在成员变量的声明和显式赋值语句的下面
 *    - 一方面，如果构造代码块赋值在上，成员变量声明在下，逻辑上会很奇怪
 *    - 另一方面，如果对象中成员变量的赋值，依赖于构造代码块和显式赋值的代码书写顺序，容易引发错误
 * 2. 因为构造代码块最终会加入构造器，所以构造代码块当中也可以使用this关键字，表示当前对象
 *    - 但是实际使用价值和意义不大，仅作了解
 *
 *
 * 最后思考一个问题: 构造代码块中能不能给静态成员变量赋值?
 * 语法上当然是可以的,但是一般情况下静态成员变量的赋值依赖于创建对象的过程是不好的
 * 如果想要使用代码块在类加载时期完成静态成员变量的赋值,需要使用静态代码块
 *
 */
public class Demo {
    public static void main(String[] args) {
        /*Student s = new Student(18);
        System.out.println(s.age); //18*/

        //Student s = new Student();
        //System.out.println(s.age);

        /*for (int i = 0; i < 5; i++) {
            new Student();
        }
        for (int i = 0; i < 8; i++) {
            new Student(20);
        }
        System.out.println(Student.count);*/
    }
}

class Student {
    static int count = 0;

    int age = 20;
    String name;

    //定义在成员位置,使用{},就是构造代码块
    {
        //this(18);
        age = 10;
        Student.count++;
        //构造代码块中写代码,基本相当于在构造器里写代码
        System.out.println(this);
    }

    public Student() {
    }

    public Student(int age) {
        this.age = age;
    }

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }
}
